package com.citywithincity.pay;

import java.io.IOException;
import java.util.Date;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.citywithincity.api.ApiConfig;
import com.citywithincity.db.Db;
import com.citywithincity.lock.Lock;
import com.citywithincity.lock.LockFactory;
import com.citywithincity.lock.impl.ReentrantLockFactory;
import com.citywithincity.utils.Sequence;
import com.damai.error.ApiError;

/**
 * 
 * @author Randy
 *
 */
public abstract class PayAction<T extends SysOrder, SUBMIT_RESULT, RESULT> implements IPaySubmit<SUBMIT_RESULT, T> {
	/**
	 * 任务能否可以被取消， 成功状态返回true
	 * 
	 * @param order
	 * @return
	 */
	protected abstract boolean canCancelOrder(T order) throws IOException, PayActionException;

	/**
	 * 撤销订单
	 * 
	 * @param order
	 */
	protected abstract void cancelOrder(T order) throws IOException, PayActionException;

	/**
	 * 当成功之后将order的相关属性值设置一下就好了
	 * 
	 * @param result
	 * @param order
	 */
	protected abstract RESULT onSubmitSuccess(SUBMIT_RESULT result, T order);

	/**
	 * 获取最终结果
	 * 
	 * @param order
	 * @return
	 */
	protected abstract RESULT getSuccess(T order);

	/*
	 * // const NO_PAY = 0; //已经付款 const PAY = 1; //已经退款 const REFUND = 2; //完成
	 * const FINISHED = 3; //领完发票 const INVOCE = 4;
	 */

	protected Class<T> clazz;
	protected String key;
	protected int businessId;
	protected String channel;
	protected LockFactory lockFactory;
	
	

	public PayAction(String key, Class<T> clazz, int businessId, String channel) {
		this.key = key;
		this.clazz = clazz;
		this.channel = channel;
		this.businessId = businessId;
		PayFactory.registerAction(businessId, this);
		
		lockFactory = new ReentrantLockFactory();
		
	}

	/**
	 * 
	 * @param id
	 * @param type
	 * @param order
	 * @param customer
	 * @param ip
	 * @return
	 * @throws IOException
	 * @throws PayException
	 */
	@SuppressWarnings("unchecked")
	public Object prePay(String id, int type, HttpServletRequest request) throws PayException, IOException {
		SysOrder order = Db.ar(clazz).get(id);
		if (order == null) {
			throw new ApiError("找不到本订单");
		}
		if(order.getPayStatus() > 0){
			throw new ApiError("订单已经支付，请勿重复支付");
		}
		IPay pay = PayFactory.getPay(businessId, type, channel);
		if (pay == null) {
			throw new ApiError("支付方式不存在");
		}
		Object result = pay.prePay(order, order.orderId(), order.getTitle(), ApiConfig.debug() ? 1 : order.getFee(),
				businessId, type, channel, request);
		order.setPayType(type);
		Db.ar(clazz).update((T) order);
		return result;

	}

	/**
	 * 
	 * @param order
	 * @return
	 */
	public String createOrder(T order) {
		String outId = String.valueOf(Sequence.getUniqueId());
		order.orderId(outId);
		order.setCreateTime(new Date());
		Db.ar(clazz).insert(order);
		return outId;
	}

	private IPay overrideGetPay(T order) {
		return overrideGetPay(order, order.getPayType());
	}

	protected IPay overrideGetPay(T order, int type) {
		return PayFactory.getPay(businessId, type, channel);
	}

	/**
	 * 获取同步的key
	 * @param id
	 * @return
	 */
	protected String getSyncKey(String outerId){
		if(outerId.contains("-")){
			String[] arr = outerId.split("-");
			outerId = arr[1];
		}
		return outerId;
	}
	
	/**
	 * 执行
	 * 
	 * @param id
	 * @return
	 * @throws PayActionException
	 */
	public void execute(String id, String platId, int type) throws IOException, PayActionException {
		//这里需要使用同意吧所
		Lock lock = lockFactory.getLock(getSyncKey(id));
		lock.lock();
		try{
			// 取出订单
			// 检查支付状态
			T order = getOrder(id);
			if (order == null) {
				return;
			}
			// 去取支付状态
			IPay pay = overrideGetPay(order, type);
			if (pay == null) {
				return;
			}
			// 查看现在的状态
			boolean changed = false;
			if (order.getPlatId() == null || !order.getPlatId().equals(platId)) {
				order.setPlatId(platId);
				changed = true;
			}
			switch (order.getPayStatus()) {
			case OrderStatus.OrderStatus_NoPay: {
				// 将状态设置为正在提交
				setSubmitting(order);
				trySubmitOrder(order);
				setPaied(order);
				break;
			}
			case OrderStatus.OrderStatus_Submiting:
			case OrderStatus.OrderStatus_SubmitError: {
				// 如果已经提交错误了，直接提示错误
				if(!retryOnTimeout()){
					return;
				}
				// 系统内部订单
				SUBMIT_RESULT innerOrder = queryInnerOrder(order);
				if (innerOrder == null) {
					// 表示还没有创建成功,应该和submit的结果一样
					trySubmitOrder(order);
				} else {
					onSubmitSuccess(innerOrder, order);
				}
				setPaied(order);
				return;
			}
			case OrderStatus.OrderStatus_Success: {
				// 如果已经成功了
				if (changed) {
					Db.ar(clazz).update(order);
				}
				return;
			}
			default: {
				return;
			}
			}
		}finally{
			lock.unlock();
		}
		
	

	}
	
	

	private void trySubmitOrder(T order) throws IOException, PayActionException {
		try {
			SUBMIT_RESULT result = submit(order);
			onSubmitSuccess(result, order);
		} catch (PayActionException e) {
			setSubmitError(e.getMessage(), order);
			throw e;
		} catch (IOException e) {
			if(!retryOnTimeout()){
				setSubmitError("创建订单超时", order);
			}
			throw e;
		} catch(Throwable e){
			setSubmitError(e.getMessage(), order);
			throw new PayActionException("创建订单失败，请联系客服");
		}
	}

	/**
	 * 超时是否重发
	 * @return
	 */
	protected boolean retryOnTimeout() {
		return true;
	}

	protected SUBMIT_RESULT queryInnerOrder(T order) {
		return null;
	}

	public String getOuterId(int type, String id, T order) {
		return id;
	}

	/**
	 * 获取支付信息
	 * 
	 * @param id
	 * @param json
	 * @return
	 * @throws PayException
	 * @throws IOException
	 * @throws PayActionException
	 */
	public RESULT getPayInfo(String id, Map<String, Object> json)
			throws PayException, IOException, PayActionException {

		//这里需要使用同意吧所
		Lock lock = lockFactory.getLock(id);
		lock.lock();
		try{
			// 取出订单
			// 检查支付状态
			T order = getOrder(id);
			if (order == null) {
				throw new PayActionException("订单不存在");
			}
			// 去取支付状态
			IPay pay = overrideGetPay(order); // PayFactory.getPay(businessId,
												// order.getPayType(),channel);
			if (pay == null) {
				throw new PayActionException("支付方式不存在");
			}
			int status = order.getPayStatus();
			if (status == OrderStatus.OrderStatus_Success) {
				return getSuccess(order);
			}
			switch (status) {
			case OrderStatus.OrderStatus_NoPay: {
				PayStatus payStatus = pay.queryClientNotifyOrderStatus(getOuterId(order.getPayType(), id, order), order,
						json);
				if (!PayStatus.PayStatus_Success.equals(payStatus)) {
					if (PayStatus.PayStatus_NotFound.equals(payStatus)) {
						throw new PayActionException("未发现支付订单");
					}
					if (PayStatus.PayStatus_Close.equals(payStatus)) {
						throw new PayActionException("支付订单已经关闭");
					}
					if (PayStatus.PayStatus_Error.equals(payStatus)) {
						throw new PayActionException("支付失败，请重新下单");
					}
					if (PayStatus.PayStatus_NoPay.equals(payStatus)) {
						throw new PayActionException("本订单未支付，请支付");
					}
					if (PayStatus.PayStatus_Paying.equals(payStatus)) {
						throw new PayActionException("支付中，请稍后重试");
					}
					if (PayStatus.PayStatus_Refund.equals(payStatus)) {
						throw new PayActionException("本订单已经退款");
					}

					if (PayStatus.PayStatus_RefundError.equals(payStatus)) {
						throw new PayActionException("订单退款失败");
					}
					if (PayStatus.PayStatus_Refunding.equals(payStatus)) {
						throw new PayActionException("订单退款中，请稍后...");
					}
					throw new PayActionException("非法订单状态");
				}
				//如果是还没有提交的，则进行提交
				setSubmitting(order);
				trySubmitOrder(order);
				setPaied(order);
				return getSuccess(order);
			}
			case OrderStatus.OrderStatus_Submiting:
			case OrderStatus.OrderStatus_SubmitError: {
				//如果是这样，直接不发送，提示错误
				if(!retryOnTimeout()){
					throw new ApiError("订单创建失败,请联系客服");
				}
				// 系统内部订单
				SUBMIT_RESULT innerOrder = queryInnerOrder(order);
				if (innerOrder == null) {
					// 表示还没有创建成功,应该和submit的结果一样
					trySubmitOrder(order);
				} else {
					onSubmitSuccess(innerOrder, order);
				}
				setPaied(order);
				return getSuccess(order);
			}

			default:
				throw new PayActionException("状态错误");
			}
		}finally{
			lock.unlock();
		}
		
	}

	protected void setRefund(T order) {
		order.setPayStatus(OrderStatus.OrderStatus_Refund);
		order.setRefundTime(new Date());
		Db.ar(clazz).update(order);
	}

	protected void setSubmitting(T order) {
		order.setPayStatus(OrderStatus.OrderStatus_Submiting);
		Db.ar(clazz).update(order);
	}

	protected T getRefundOrder(String id) {
		return getOrder(id);
	}

	/**
	 * 执行退款
	 * 
	 * @return
	 * @throws PayActionException
	 */
	public synchronized void refund(String id) throws IOException, PayException, PayActionException {

		T order = getRefundOrder(id);
		if (order == null) {
			throw new ApiError("订单不存在");
		}
		// 首先查看订单状态，是否可以撤销
		switch (order.getPayStatus()) {
		case OrderStatus.OrderStatus_CancelOrderSuccess: {
			// 这里执行退款
			tryRefund(order);
		}
			break;
		case OrderStatus.OrderStatus_Success: {
			// 设置正在撤销
			setRefunding(order);
		}
		case OrderStatus.OrderStatus_Refunding: {
			// 判断订单是否已经被撤销
			// 这里撤销订单并且退款
			if (canCancelOrder(order)) {
				tryCancelOrder(order);
			}
			tryRefund(order);
		}
			break;
		/**
		 * 这里直接报错，必须由人工处理
		 */
		case OrderStatus.OrderStatus_CancelOrderError:
		case OrderStatus.OrderStatus_RefundError:
			throw new PayActionException(order.getUserError());
			/**
			 * 下面错误的状态直接返回
			 */
			/*
			 * case OrderStatus.OrderStatus_SubmitError: case
			 * OrderStatus.OrderStatus_NoPay: case
			 * OrderStatus.OrderStatus_Submiting: case
			 * OrderStatus.OrderStatus_Refund:
			 */
		default:
			break;
		}
	}

	/**
	 * 这里需要尝试多次
	 * 
	 * @param order
	 * @throws IOException
	 * @throws PayException
	 */

	protected void tryRefund(T order) throws IOException, PayException {
		int count = 0;
		for(int i=0; i < 3; ++i) {
			// 查看是否是成功付款的
			IPay pay = overrideGetPay(order);// PayFactory.getPay(businessId,
												// order.getPayType(),channel);
			try {
				String outerId = getOuterId(order.getPayType(), order.orderId(), order);
				PayStatus payStatus = pay.queryStatus(outerId, order, true);
				if (payStatus == PayStatus.PayStatus_Success) {
					
					if(order.getPayType() == PayType.PayType_WX){
						logger.info("===========正在执行退款，支付类型为:微信支付");
						pay.refund(order, outerId, outerId, ApiConfig.debug() ? 1 : order.getFee(), true);
					}else{
						logger.info("===========正在执行退款，支付类型为:未微信支付");
						pay.refund(order, outerId, outerId, order.getFee(), true);
					}
					
					setRefund(order);
					return;
				} else if (payStatus == PayStatus.PayStatus_Refund) {
					if (order.getPayStatus() != OrderStatus.OrderStatus_Refund) {
						setRefund(order);
					}
					return;
				} else {
					// 其他状态都是错误的
					throw new ApiError("退款失败，支付状态为:" + payStatus.name());
				}
			} catch (PayException e) {
				setRefundError(order, e.getMessage() + ",请联系客服人工退款");
				throw e;
			} catch (IOException e) {
				++count;
				if (count >= 2) {
					throw e;
				}
			} catch(Throwable t){
				logger.info("退款发生其他异常",t);
				setRefundError(order, t.getMessage() + ",请联系客服人工退款");
				break;
			}
		}

	}

	private void setRefundError(T order, String userError) {
		order.setPayStatus(OrderStatus.OrderStatus_RefundError);
		order.setUserError(userError);
		Db.ar(clazz).update(order);
	}

	protected void tryCancelOrder(T order) throws IOException, PayActionException {
		// 首先执行系统撤销订单
		try {
			cancelOrder(order);
			setCanceled(order);
		} catch (PayActionException e) {
			// 如果发生了不可恢复的错误，则应该记录下来
			setCancelOrderError(e.getMessage(), order);
			throw e;
		}
	}

	protected void setCanceled(T order) {
		order.setPayStatus(OrderStatus.OrderStatus_CancelOrderSuccess);
		Db.ar(clazz).update(order);
	}

	protected void setRefunding(T order) {
		order.setPayStatus(OrderStatus.OrderStatus_Refunding);
		Db.ar(clazz).update(order);
	}

	/**
	 * 设置撤销订单错误
	 * 
	 * @param message
	 * @param order
	 */
	protected void setCancelOrderError(String message, T order) {
		order.setPayStatus(OrderStatus.OrderStatus_CancelOrderError);
		order.setUserError(message);
		Db.ar(clazz).update(order);
	}

	/**
	 * 用户提交失败错误
	 * 
	 * @param error
	 * @param order
	 */
	public void setSubmitError(String error, T order) {
		order.setUserError(error);
		order.setPayStatus(OrderStatus.OrderStatus_SubmitError);
		Db.ar(clazz).update(order);
	}

	/**
	 * 设置已经支付
	 * 
	 * @param order
	 */
	public void setPaied(T order) {
		order.setPayTime(new Date());
		order.setPayStatus(OrderStatus.OrderStatus_Success);
		Db.ar(clazz).update(order);
	}

	/**
	 * 
	 * @param id
	 * @return
	 */
	protected T getOrder(String id) {
		return Db.ar(clazz).get(id);
	}

	private static final Log logger = LogFactory.getLog(PayAction.class);

	public boolean handleServerNotify(int type, int businessId, String channel, HttpServletRequest request)
			throws IOException {
		// 解析
		IPay pay = PayFactory.getPay(businessId, type, channel);
		if (pay == null) {
			logger.error("pay is null");
			return true;
		} else {
			// 解析出来
			try {
				OrderInfo orderInfo = pay.parseOrder(request);
				if (orderInfo == null) {
					/**
					 * 没有返回数据，表示状态不符合,不应该处理
					 */
					return true;
				}
				if (orderInfo.getPayStatus() == PayStatus.PayStatus_Success) {
					try {
						execute(orderInfo.getOrderId(), orderInfo.getPlatId(), type);
						return true;
					} catch (IOException e) {
						return false;
					} catch (PayActionException e) {
						return true;
					}
				} else {
					return true;
				}
			} catch (PayException e) {
				/**
				 * 签名错误比如
				 */
				return false;
			}

		}
	}

}
